package com.xgk.boot.module.core.service.product;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
import com.xgk.boot.framework.common.constant.ErrorCodeConstants;
import com.xgk.boot.framework.common.enums.CommonStatusEnum;
import com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil;
import com.xgk.boot.framework.common.pojo.PageResult;
import com.xgk.boot.framework.common.util.object.BeanUtils;
import com.xgk.boot.framework.mybatis.core.dataobject.BaseDO;
import com.xgk.boot.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.xgk.boot.module.core.controller.admin.price.vo.PriceRuleVO;
import com.xgk.boot.module.core.controller.admin.product.vo.product.ProductInfoVO;
import com.xgk.boot.module.core.controller.admin.product.vo.product.ProductPageReqVO;
import com.xgk.boot.module.core.controller.admin.product.vo.product.ProductPageRespVO;
import com.xgk.boot.module.core.controller.admin.product.vo.product.ProductSaveReqVO;
import com.xgk.boot.module.core.dal.entity.channel.ApiServiceDO;
import com.xgk.boot.module.core.dal.entity.channel.ServiceProviderDO;
import com.xgk.boot.module.core.dal.entity.price.PriceRuleDO;
import com.xgk.boot.module.core.dal.entity.product.ProductCustomerDO;
import com.xgk.boot.module.core.dal.entity.product.ProductDO;
import com.xgk.boot.module.core.dal.entity.product.ProductPriceRuleDO;
import com.xgk.boot.module.core.dal.iservice.ProductService;
import com.xgk.boot.module.core.dal.mapper.channel.ServiceProviderMapper;
import com.xgk.boot.module.core.dal.mapper.customer.CustomerMapper;
import com.xgk.boot.module.core.dal.mapper.product.ProductCustomerMapper;
import com.xgk.boot.module.core.dal.mapper.product.ProductMapper;
import com.xgk.boot.module.core.dal.redis.RedisKeyConstants;
import com.xgk.boot.module.core.enums.biz.PriceRuleTypeEnum;
import com.xgk.boot.module.core.service.channel.ServiceProviderAPIServiceImpl;
import com.xgk.boot.module.core.service.channel.ServiceProviderServiceImpl;
import com.xgk.boot.module.core.service.price.PriceRuleServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static com.xgk.boot.framework.common.constant.ErrorCodeConstants.*;
import static com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xgk.boot.module.core.enums.LogRecordConstants.*;

@Slf4j
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, ProductDO> implements ProductService {

    @Resource
    private ProductMapper productMapper;
    @Resource
    private CustomerMapper customerMapper;
    @Resource
    private PriceRuleServiceImpl priceRuleService;
    @Resource
    private ProductPriceRuleServiceImpl productPriceRuleService;
    @Resource
    private ServiceProviderServiceImpl spService;
    @Resource
    private ServiceProviderAPIServiceImpl spApiService;
    @Resource
    private ServiceProviderMapper serviceProviderMapper;
    @Resource
    private CacheManager cacheManager;
    @Resource
    private ProductServiceImpl self;
    @Resource
    private ProductCustomerMapper productCustomerMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_PRODUCT_TYPE, subType = SYSTEM_PRODUCT_CREATE_TYPE,  bizNo = "{{#product.prId}}",success = SYSTEM_PRODUCT_CREATE_SUCCESS)
    public Long createProduct(ProductSaveReqVO createReqVO) {

//        ApiServiceDO apiServiceDO = apiServiceMapper.selectById(createReqVO.getAsId());
//        if(apiServiceDO ==null){
//            throw exception(API_NOT_EXISTS, createReqVO.getAsId());
//        }
        validateProductDuplicate(createReqVO.getPrName(), createReqVO.getPrCode());
        validatePriceRuleConfig(createReqVO.getBaseTradingFees(),createReqVO.getExtraTradingFees());
        //保存产品主表
        ProductDO api = factoryProduct(createReqVO);
        this.saveOrUpdate(api);
        //保存价格规则
        List<ProductPriceRuleDO> productPriceRuleDOS = factoryProductPriceRules(api, createReqVO);
        productPriceRuleService.saveBatch(productPriceRuleDOS);
        // 3. 记录操作日志上下文
        LogRecordContext.putVariable("product", api);
        return api.getPrId();
    }

    private List<ProductPriceRuleDO> factoryProductPriceRules(ProductDO productDO,ProductSaveReqVO createReqVO){
        List<ProductPriceRuleDO> list =new ArrayList<>();
        List<PriceRuleVO> baseTradingFees = createReqVO.getBaseTradingFees();
        for (PriceRuleVO baseTradingFee : baseTradingFees) {
            ProductPriceRuleDO productPriceRuleDO =new ProductPriceRuleDO();
            productPriceRuleDO.setPId(productDO.getPrId());
            productPriceRuleDO.setPCode(productDO.getPrCode());
            productPriceRuleDO.setPrId(baseTradingFee.getId());
            productPriceRuleDO.setPrCode(baseTradingFee.getRuleCode());
            productPriceRuleDO.setPrType(PriceRuleTypeEnum.Basic_Rate.getType());
            list.add(productPriceRuleDO);
        }

        List<PriceRuleVO> extraTradingFees = createReqVO.getExtraTradingFees();
        if(CollUtil.isEmpty(extraTradingFees)){
           return list;
        }
        for (PriceRuleVO extraTradingFee : extraTradingFees) {
            ProductPriceRuleDO pPriceRuleDO =new ProductPriceRuleDO();
            pPriceRuleDO.setPId(productDO.getPrId());
            pPriceRuleDO.setPCode(productDO.getPrCode());
            pPriceRuleDO.setPrId(extraTradingFee.getId());
            pPriceRuleDO.setPrCode(extraTradingFee.getRuleCode());
            pPriceRuleDO.setPrType(PriceRuleTypeEnum.Extras_Fee.getType());
            list.add(pPriceRuleDO);
        }
        return list;
    }
    private ProductDO factoryProduct(ProductSaveReqVO createReqVO){
        ProductDO api = BeanUtils.toBean(createReqVO, ProductDO.class);
        //设置默认为物流商品
        api.setPrType(1);
        String sp = createReqVO.getSp();
        if(!sp.startsWith("api_")){
            throw exception(COMMON_PARAMER_EXCEPTION,"sp");
        }
        String substring = sp.substring("api_".length());
        Long spApiId = Long.valueOf(substring);
        ApiServiceDO byId = spApiService.getById(spApiId);
        if(byId ==null){
            throw exception(COMMON_INFO_EXISTS,String.format("sp_api:{%s}",sp));
        }
        ServiceProviderDO apiServiceDO = serviceProviderMapper.selectProviderByApiId(spApiId);
        if(apiServiceDO == null){
            throw exception(COMMON_INFO_EXISTS,String.format("sp_api:{%s}",sp));
        }
        api.setPrSpId(apiServiceDO.getSpId());
        api.setPrSpApiId(spApiId);
        api.setPrSpApiPd(byId.getAsCode());
        return api;
    }

    private void validatePriceRuleConfig(List<PriceRuleVO> baseTradingFees,List<PriceRuleVO> extraTradingFees){
        if(baseTradingFees == null || baseTradingFees.isEmpty()){
            throw exception(COMMON_INFO_EMPTY,"baseTradingFees");
        }
        Set<Long> ids = baseTradingFees.stream().map(PriceRuleVO::getId).collect(Collectors.toSet());
        List<PriceRuleDO> priceRuleByIds = priceRuleService.getPriceRuleByIds(ids);
        if(priceRuleByIds ==null || priceRuleByIds.size() != ids.size()){
            throw exception(COMMON_PARAMER_CONTAINS_NON_EXIST,String.format("baseTradingFee:{}", StringUtils.join(ids, " -> ")));
        }
    }

    private void validateProductDuplicate(String name, String code) {
        ProductDO api = productMapper.selectByName(name);
        if(api != null) {
            throw exception(SP_EXISTS, name);
        }
        if(StrUtil.isBlank(code)) {
            return;
        }
        api = productMapper.selectByCode(code);
        if(api != null) {
            throw exception(SP_CODE_DUPLICATE, code);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_PRODUCT_TYPE, subType = SYSTEM_PRODUCT_UPDATE_TYPE, bizNo = "{{#product.prId}}", success = SYSTEM_PRODUCT_UPDATE_SUCCESS)
    @CacheEvict(value = RedisKeyConstants.PRODUCT,key = "#updateReqVO.prId")
    public void updateProduct(ProductSaveReqVO updateReqVO) {
        ProductDO api = validateProductForUpdate(updateReqVO.getPrId());
//        validateSpDuplicate(updateReqVO.getAsName(),updateReqVO.getAsCode());
        ProductDO updateObj = factoryProduct(updateReqVO);
        //产品编码不允许修改
        updateObj.setPrCode(api.getPrCode());
        productMapper.updateById(updateObj);

        updateProductPriceRules(updateObj,updateReqVO);
        // 3. 记录操作日志上下文
        LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(api, ProductSaveReqVO.class));
        LogRecordContext.putVariable("product", updateObj);
        cacheEvict(updateObj.getPrCode());
    }


    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = SYSTEM_PRODUCT_TYPE, subType = SYSTEM_PRODUCT_DELETE_TYPE, bizNo = "{{#id}}", success = SYSTEM_PRODUCT_DELETE_SUCCESS)
    @CacheEvict(value = RedisKeyConstants.PRODUCT,key = "#id")
    public boolean deleteProduct(Long id){
        ProductDO byId = this.getById(id);
        if(id ==null){
            return true;
        }
        if(byId.getPrStatus().equals(CommonStatusEnum.ENABLE.getStatus())){
            throw exception(COMMON_INFO_NO_DELETE,byId.getPrName()+" status is enable");
        }
        //todo：判断没有客户关联，没有关联是才能删除


        //先删除价格设计关联,如果商品逻辑删除，这里可以暂不删除
//        LambdaQueryWrapperX<ProductPriceRuleDO> eq = new LambdaQueryWrapperX<ProductPriceRuleDO>().eq(ProductPriceRuleDO::getPId, productDO.getPrId());
//        productPriceRuleService.remove(eq);
        //删除商品设置
        boolean deleteStatus = this.removeById(id);
        LogRecordContext.putVariable("product", byId);
        cacheEvict(byId.getPrCode());
        return deleteStatus;
    }


    /**
     * 更新商品收费规则
     * @param productDO
     * @param createReqVO
     * @return
     */
    private void updateProductPriceRules(ProductDO productDO,ProductSaveReqVO createReqVO){
        List<ProductPriceRuleDO> list =new ArrayList<>();
        List<PriceRuleVO> baseTradingFees = createReqVO.getBaseTradingFees();
        for (PriceRuleVO baseTradingFee : baseTradingFees) {
            ProductPriceRuleDO productPriceRuleDO =new ProductPriceRuleDO();
            productPriceRuleDO.setPId(productDO.getPrId());
            productPriceRuleDO.setPCode(productDO.getPrCode());
            productPriceRuleDO.setPrId(baseTradingFee.getId());
            productPriceRuleDO.setPrCode(baseTradingFee.getRuleCode());
            productPriceRuleDO.setPrType(PriceRuleTypeEnum.Basic_Rate.getType());
            list.add(productPriceRuleDO);
        }

        List<PriceRuleVO> extraTradingFees = createReqVO.getExtraTradingFees();
        if(!CollUtil.isEmpty(extraTradingFees)){
            for (PriceRuleVO extraTradingFee : extraTradingFees) {
                ProductPriceRuleDO pPriceRuleDO =new ProductPriceRuleDO();
                pPriceRuleDO.setPId(productDO.getPrId());
                pPriceRuleDO.setPCode(productDO.getPrCode());
                pPriceRuleDO.setPrId(extraTradingFee.getId());
                pPriceRuleDO.setPrCode(extraTradingFee.getRuleCode());
                pPriceRuleDO.setPrType(PriceRuleTypeEnum.Extras_Fee.getType());
                list.add(pPriceRuleDO);
            }
        }
        LambdaQueryWrapperX<ProductPriceRuleDO> eq = new LambdaQueryWrapperX<ProductPriceRuleDO>().eq(ProductPriceRuleDO::getPId, productDO.getPrId());
        productPriceRuleService.remove(eq);
        productPriceRuleService.saveBatch(list);
//        return list;
    }

    public  ProductDO validateProductForUpdate(Long id) {
        ProductDO api = productMapper.selectById(id);
        if(api == null) {
            throw exception(SP_NOT_EXISTS);
        }

        return api;
    }

    @Override
    public PageResult<ProductPageRespVO> getProductPage(ProductPageReqVO pageReqVO) {
        PageResult<ProductDO> result = productMapper.selectPage(pageReqVO);
        List<ProductDO> data = result.getData();
        if(CollUtil.isEmpty(data)){
            return new PageResult<ProductPageRespVO>(Lists.newArrayList(), result.getTotal(),pageReqVO.getCurrent(),pageReqVO.getPageSize());
        }
        Set<Long> collect = data.stream().map(ProductDO::getPrSpId).collect(Collectors.toSet());
        LambdaQueryWrapperX<ServiceProviderDO> in = new LambdaQueryWrapperX<ServiceProviderDO>().in(ServiceProviderDO::getSpId, collect);
        List<ServiceProviderDO> list = spService.list(in);
        Map<Long, String> collect1 = list.stream().collect(Collectors.toMap(ServiceProviderDO::getSpId, ServiceProviderDO::getSpName));
        List<ProductPageRespVO> productPageRespVOS = BeanUtils.toBean(data, ProductPageRespVO.class);
        for (ProductPageRespVO productPageRespVO : productPageRespVOS) {
            productPageRespVO.setSpName(collect1.get(productPageRespVO.getPrSpId()));
        }
        return new PageResult<ProductPageRespVO>(productPageRespVOS, result.getTotal(),pageReqVO.getCurrent(),pageReqVO.getPageSize());
    }


    /**
     * 根据产品code获取产品信息
     */
    @Cacheable(value = RedisKeyConstants.PRODUCT_CODE, key = "#code", unless = "#result == null")
    public ProductDO getProductCacheByCode(String code) {
        if(StrUtil.isBlank(code)){
            return null;
        }
        ProductDO productDO = productMapper.selectByCode(code);
        if(productDO == null){
            return null;
        }
        return productDO;
    }

    /**
     * 根据产品code获取产品对应spi的信息
     * @param productCode
     */
    public ApiServiceDO getApiServiceByProductCode(String productCode) {
        if(StrUtil.isBlank(productCode)){
            return null;
        }
        ProductDO productDO = this.getProductCacheByCode(productCode);
        if(productDO == null){
            log.warn("未找到对应的产品，ProductCode: {}", productCode);
            throw  ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EXISTS,"product_code");
        }
        Long prSpApiId = productDO.getPrSpApiId();
        if(prSpApiId == null){
            log.warn("未找到对应的产品，ProductCode: {} 关联的spi信息", productCode);
            throw  ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EXISTS, productCode + "_prSpApiId");
        }
        ApiServiceDO apiServiceDO = spApiService.getById(prSpApiId);
        if(apiServiceDO == null){
            log.warn("未找到对应的spi，spi_id: {}", prSpApiId);
            throw  ServiceExceptionUtil.exception(ErrorCodeConstants.API_NOT_EXISTS);
        }
        return apiServiceDO;
    }


    @Override
    @CacheEvict(value = RedisKeyConstants.PRODUCT, key = "#asId")
    public void activeProduct(Long asId, Integer asStatus) {
        ProductDO api = validateProductForUpdate(asId);
        if(asStatus == CommonStatusEnum.ENABLE.getStatus()) {
            api.setPrStatus(CommonStatusEnum.DISABLE.getStatus());
        } else {
            api.setPrStatus(CommonStatusEnum.ENABLE.getStatus());
        }
        productMapper.updateById(api);
        cacheEvict(api.getPrCode());
    }


    public List<ProductDO> getProductSelectList() {
        List<ProductDO> all = productMapper.selectList(ProductDO::getPrStatus, CommonStatusEnum.ENABLE.getStatus());
        return all;
    }

    /**
     * 通过商品code 获取商品配置详情
     * @param code
     * @return
     */
    public ProductInfoVO getProductInfoByCode(String code){
        ProductDO productByCode = self.getProductCacheByCode(code);
        return self.getProductInfoCacheById(productByCode.getPrId());
    }


//    @Cacheable(value = RedisKeyConstants.PRODUCT, key = "#id", unless = "#result == null")
    public ProductInfoVO getProductInfoCacheById(Long id){
        ProductDO byId = this.getById(id);
        if(byId ==null){
            return null;
        }
        ProductInfoVO productInfoVO = BeanUtils.toBean(byId, ProductInfoVO.class);
        productInfoVO.setSp("api_"+productInfoVO.getPrSpApiId());
        List<PriceRuleDO> basePriceRules = priceRuleService.getBaseMapper().selectPriceRuleByProductAndType(id, PriceRuleTypeEnum.Basic_Rate.getType());
        if(CollUtil.isNotEmpty(basePriceRules)){
            List<PriceRuleVO> priceRuleVOS = BeanUtils.toBean(basePriceRules, PriceRuleVO.class);
            productInfoVO.setBaseTradingFees(priceRuleVOS);
        }

        List<PriceRuleDO> priceRuleDOS = priceRuleService.getBaseMapper().selectPriceRuleByProductAndType(id, PriceRuleTypeEnum.Extras_Fee.getType());
        if(CollUtil.isNotEmpty(priceRuleDOS)){
            List<PriceRuleVO> priceRuleVOS = BeanUtils.toBean(priceRuleDOS, PriceRuleVO.class);
            productInfoVO.setExtraTradingFees(priceRuleVOS);
        }
        return productInfoVO;
    }

    @Override
    public List<ProductDO> listProduct(ProductPageReqVO query) {
        LambdaQueryWrapperX<ProductDO> queryWrapperX = new LambdaQueryWrapperX<ProductDO>().likeIfPresent(ProductDO::getPrName, query.getPrName())
                .likeIfPresent(ProductDO::getPrCode, query.getPrCode())
                .eqIfPresent(ProductDO::getPrStatus, query.getPrStatus())
                .betweenIfPresent(BaseDO::getCreateTime, new Instant[]{query.getStartTime(), query.getEndTime()});
        List<ProductDO> list = this.list(queryWrapperX);
        return list;
    }

    @Override
    public List<Map> getProductByUserName(String userName) {
        return productMapper.selectProductsByCustomerName(userName);
    }



    //根据客户code获取产品
    public List<ProductDO> listProductCodeByCustomer(String customerCode  ) {
        LambdaQueryWrapperX<ProductCustomerDO> eq = new LambdaQueryWrapperX<ProductCustomerDO>()
                .eq(ProductCustomerDO::getCustomerCode, customerCode);
        List<ProductCustomerDO> list = productCustomerMapper.selectList(eq);
        if(CollUtil.isEmpty(list)){
            return Lists.newArrayList();
        }
        LambdaQueryWrapperX<ProductDO> prdEq = new LambdaQueryWrapperX<ProductDO>();
        prdEq.in(ProductDO::getPrId, list.stream().map(ProductCustomerDO::getProductId).collect(Collectors.toList()))
                .eq(ProductDO::getPrStatus, CommonStatusEnum.ENABLE.getStatus());
//                .select(ProductDO::getPrId,ProductDO::getPrName,ProductDO::getPrCode,ProductDO::getPrStatus);
        return this.list(prdEq);
    }

    /**
     * 手动失效缓存
     */
    private void cacheEvict(String code) {
        if(StrUtil.isBlank(code)){
            return;
        }
        cacheManager.getCache(RedisKeyConstants.PRODUCT_CODE).evict(code);
    }
}
